package com.boboPlanet.filter;

import com.alibaba.fastjson.JSON;
import com.boboPlanet.comm.component.JwtConfig;
import com.boboPlanet.comm.component.JwtProperties;
import com.boboPlanet.comm.entity.dto.LoginUserInfoDTO;
import com.boboPlanet.comm.enums.GatewayConstance;
import com.boboPlanet.comm.enums.LoginUserErrorEnum;
import com.boboPlanet.comm.rabbitmq.RabbitMqRoutingConfig;
import com.boboPlanet.comm.rabbitmq.producer.PlanetMqTopic;
import com.boboPlanet.service.PageExceptionHandler;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;

/**
 * @Description: TODO jwt鉴权校验过滤器
 * @Author: mis_wu
 * @Date: 2023/6/20
 * @Company planet
 **/
@Component
@Slf4j
public class JwtCheckFilter implements GlobalFilter, Ordered {

    @Resource
    private JwtProperties jwtProperties;

    @Resource
    private JwtConfig jwtConfig;

    @Resource
    private PageExceptionHandler pageExceptionHandler;

    @Resource
    private PlanetMqTopic planetMqTopic;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String requestPath = request.getURI().getPath();
        log.info("request into gateway jwtFilter, current path is: {}", requestPath);

        //请求头放行判断,判断上一个过滤器是否放行请求
        String allow = request.getHeaders().getFirst(GatewayConstance.ALLOW_FILTER);
        if (String.valueOf(true).equals(allow)){
            return chain.filter(exchange);
        }

        //签名白名单，yml配置
        List<String> jwtSkipUrl = jwtProperties.getSkipValidUrl();
        if (!jwtSkipUrl.isEmpty() && jwtSkipUrl.contains(requestPath)){

            log.info("current request path not jwt check,requestHeader is: {}",allow);

            this.setHttpHeader(request,exchange);

            return chain.filter(exchange);
        }
        //开启jwt鉴权
        if (jwtProperties.getEnabled()){
            //校验签名信息,请求头字段，可取配置文件，也可去枚举定义，但两边名称要和前端保持一致
            //String signature = request.getHeaders().getFirst(GatewayConstance.AUTH);
            String signature = request.getHeaders().getFirst(jwtProperties.getAuthHeader());

            if (StringUtils.isBlank(signature)){
                return pageExceptionHandler.pageExceptionHandler(response, HttpStatus.NON_AUTHORITATIVE_INFORMATION, LoginUserErrorEnum.USER_AUTHOR_FAIL.getDesc());
            }

            Claims claim = jwtConfig.getTokenClaim(signature);
            if (ObjectUtils.isEmpty(claim)){
                return pageExceptionHandler.pageExceptionHandler(response, HttpStatus.NON_AUTHORITATIVE_INFORMATION,LoginUserErrorEnum.USER_AUTHOR_FAIL.getDesc());
            }
            String subject = claim.getSubject();
            //使用 userInfoDTO鉴权也可，考虑不用 token，繁琐
            LoginUserInfoDTO userInfoDTO = JSON.parseObject(subject, LoginUserInfoDTO.class);
            if (!jwtConfig.isTokenExpired(signature)){
                //发mq或直接修改
                planetMqTopic.queue(RabbitMqRoutingConfig.gateway_login_expire_queue)
                        .exchange(RabbitMqRoutingConfig.gateway_login_expire_exchange)
                        .routingKey(RabbitMqRoutingConfig.gateway_login_expire_routing_key)
                        .sendMsg(userInfoDTO);
                return pageExceptionHandler.pageExceptionHandler(response,HttpStatus.NON_AUTHORITATIVE_INFORMATION, LoginUserErrorEnum.AUTHORITATIVE_EXPIRE.getDesc());
            }
        }
        log.info("into JwtCheckFilter, check route jwt info!!!");
        return chain.filter(exchange);
    }


    private void setHttpHeader(ServerHttpRequest request,ServerWebExchange exchange){
        ServerHttpRequest build = request.mutate().headers(httpHeaders ->
                httpHeaders.set(GatewayConstance.ALLOW_FILTER, String.valueOf(true))
        ).build();

        exchange.mutate().request(build);
    }

    @Override
    public int getOrder() {
        return 2;
    }
}
